var EventTarget = (function () {
  "use strict";

  var
    PREFIX = "@@",
    EventTarget = {},
    descriptor = {
      // in ES5 does not bother with enumeration
      configurable: true,
      value: null
    },
    defineProperty = Object.defineProperty ||
    function defineProperty(obj, prop, desc) {
      // in ES3 obj.hasOwnProperty() in for/in loops
      // is still mandatory since there's no way
      // to simulate non enumerable properties
      obj[prop] = desc.value;
    },
    indexOf = [].indexOf || function indexOf(value) {
      var i = this.length;
      while (i-- && this[i] !== value) {}
      return i;
    },
    has = EventTarget.hasOwnProperty;

  function configure(obj, prop, value) {
    descriptor.value = value;
    defineProperty(obj, prop, descriptor);
    descriptor.value = null;
  }

  function on(self, type, listener) {
    var array;
    if (has.call(self, type)) {
      array = self[type];
    } else {
      configure(self, type, array = []);
    }
    if (indexOf.call(array, listener) < 0) {
      array.push(listener);
    }
  }

  function dispatch(self, type, evt) {
    var array, current, i;
    if (has.call(self, type)) {
      //evt.target = self;
      array = self[type].slice(0);
      for (i = 0; i < array.length; i++) {
        current = array[i];
        if (typeof current === "function") {
          current.call(self, evt);
        } else if (typeof current.handleEvent === "function") {
          current.handleEvent(evt);
        }
      }
    }
  }

  function off(self, type, listener) {
    var array, i;
    if (has.call(self, type)) {
      array = self[type];
      i = indexOf.call(array, listener);
      if (-1 < i) {
        array.splice(i, 1);
        if (!array.length) {
          delete self[type];
        }
      }
    }
  }

  configure(
    EventTarget,
    "addEventListener",
    function addEventListener(type, listener) {
      on(this, PREFIX + type, listener);
    }
  );

  configure(
    EventTarget,
    "dispatchEvent",
    function dispatchEvent(evt) {
      dispatch(this, PREFIX + evt.type, evt);
    }
  );

  configure(
    EventTarget,
    "removeEventListener",
    function removeEventListener(type, listener) {
      off(this, PREFIX + type, listener);
    }
  );

  return EventTarget;

})()

export class XiamiPlayer {
  constructor(autoplay = false) {
    
    let audio = new Audio()
    audio.addEventListener('play', () => {
      this.dispatchEvent(new CustomEvent('PlayingStatusChange', {
        detail: {
          isPaused: this.isPaused()
        }
      }))
    })
    audio.addEventListener('pause', () => {
      this.dispatchEvent(new CustomEvent('PlayingStatusChange', {
        detail: {
          isPaused: this.isPaused()
        }
      }))
    })
    
    this.audio_element = audio
    
    this.playlist = []
    this.cursor = 0
    this.autoplay = autoplay
  }
  
  set autoplay(can_autoplay) {
    this.audio_element.autoplay = can_autoplay
  }
  
  get autoplay() {
    return this.audio_element.autoplay
  }
  
  previous(num = 1) {
    if (this.cursor - num > 0) {
      let track = this.playlist[this.cursor -= num]
      this.load(track.location)
    } else {
      this.dispatchEvent(new CustomEvent('PreviousMusicIsntExist'))
      //throw new Error('This is the first track, cannot go previous track.')
    }
    return this
  }
  
  next(num = 1) {
    if (this.playlist.length > this.cursor + num) {
      let track = this.playlist[this.cursor += num]
      this.load(track.location)
    } else {
      this.dispatchEvent(new CustomEvent('NextMusicIsntExist'))
      //throw new Error('This is the last track, cannot go next track.')
    }
    return this
  }
  
  get current_track() {
    return this.playlist[this.cursor]
  }
  
  _formatNumToMMSS(sec){
    function fillBlank(value){
      let str = value.toString()
      if(str.length < 2){
        str = '0' + str
      }
      return str
    }
    
    let s = sec % 60,
        m = Math.floor(sec / 60)
    
    return `${fillBlank(m)}:${fillBlank(s)}`
  }
  
  get current_time() {
    return parseInt(this.audio_element.currentTime || 0)
  }
  
  get total_time() {
    return parseInt(this.audio_element.duration || 0)
  }
  
  get current_time_MMSS() {
    return this._formatNumToMMSS(this.current_time)
  }
  
  get total_time_MMSS() {
    return this._formatNumToMMSS(this.total_time)
  }
  
  isLastMusic() {
    return this.playlist.length - 1 === this.cursor
  }
  
  isPaused() {
    return this.audio_element.paused
  }
  
  isMuted() {
    return this.audio_element.muted
  }
  
  _decryptLocation(str) {
    try {
      let a1 = parseInt(str.charAt(0)),
        a2 = str.substring(1),
        a3 = Math.floor(a2.length / a1),
        a4 = a2.length % a1,
        a5 = [],
        a6 = 0,
        a7 = '',
        a8 = '';
      for (; a6 < a4; ++a6) {
        a5[a6] = a2.substr((a3 + 1) * a6, (a3 + 1));
      }
      for (; a6 < a1; ++a6) {
        a5[a6] = a2.substr(a3 * (a6 - a4) + (a3 + 1) * a4, a3);
      }
      for (let i = 0,a5_0_length = a5[0].length; i < a5_0_length; ++i) {
        for (let j = 0,a5_length = a5.length; j < a5_length; ++j) {
          a7 += a5[j].charAt(i);
        }
      }
      a7 = decodeURIComponent(a7);
      for (let i = 0,a7_length = a7.length; i < a7_length; ++i) {
        a8 += a7.charAt(i) === '^' ? '0': a7.charAt(i);
      }
      return a8;
    } catch(e) {
      return false;
    }
  }
  
  _getHqMusicUrl(id){
    return new Promise((resolve, reject) => {
      let req = new XMLHttpRequest()
      req.open('GET', `http://www.xiami.com/song/gethqsong/sid/${id}`)
      //req.setRequestHeader('Content-Type', 'text/xml; charset=utf-8')
      req.onreadystatechange = () => {
        if (req.status === 200) {
          resolve(this._decryptLocation(JSON.parse(req.responseText).location))
        } else {
          reject(new Error(req.statusText))
        }
      }
      req.send()
    })
  }

  load(music_url = this.current_track.location) {
    if (!music_url.startsWith('http')) {
      music_url = this._decryptLocation(music_url)
    }
    this.audio_element.src = music_url
    this.dispatchEvent(new CustomEvent('MusicLoading', {
      detail: {
        info: this.current_track
      }
    }))
    return this
  }
  
  reload() {
    this.audio_element.load()
    return this
  }
  
  play() {
    this.audio_element.play()
    return this
  }
  
  pause() {
    this.audio_element.pause()
    return this
  }
  
  add(tracklist) {
    if (Array.isArray(tracklist)) {
      this.playlist.push(...tracklist)
    }
    return this
  }
  
  set volume(num_volume) {
    this.audio_element.volume = num_volume
  }
  
  get volume() { 
    return this.audio_element.volume
  }
  
  set mute(value) {
    this.audio_element.muted = value
  }
}

XiamiPlayer.prototype.addEventListener = EventTarget.addEventListener
XiamiPlayer.prototype.removeEventListener = EventTarget.removeEventListener
XiamiPlayer.prototype.dispatchEvent = EventTarget.dispatchEvent